using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;

namespace TCC.Framework
{
    /// <summary>
    /// 资源管理器
    /// </summary>
    public class ResourceManager :BaseManager
    {
        public ResourceManager(MonoBehaviour main,UpdateMode loadMode) : base()
        {
            _main = main;
            CurrentScene = SceneManager.GetActiveScene();
        }

        private MonoBehaviour _main;

        /// <summary>
        /// 所有AssetBundle资源包清单
        /// </summary>
        public AssetBundleManifest AssetBundleManifest { get; private set; }

        /// <summary>
        /// 缓存的所有AssetBundle包【AB包名称、AB包】
        /// </summary>
        public Dictionary<string, AssetBundle> AssetBundles { get; private set; } = new Dictionary<string, AssetBundle>();

        public Scene CurrentScene { get; private set; }

        public override void OnInit()
        {
            base.OnInit();
        }

        public T LoadAsset<T>(LoadResHandle<T> resHandle)where T:UnityEngine.Object
        {
            return LoadAsset<T>(resHandle.ResourcePath, resHandle.AssetPath, resHandle.AssetBundleName, resHandle.LoadMode);
        }

        /// <summary>
        /// 同步加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="resPath"></param>
        /// <param name="assetPath"></param>
        /// <param name="assetBundleName"></param>
        /// <param name="resLoadMode"></param>
        /// <returns></returns>
        public T LoadAsset<T>(string resPath,string assetPath,string assetBundleName="", ResLoadMode resLoadMode = ResLoadMode.Resourece) where T:UnityEngine.Object
        {
            if (resLoadMode == ResLoadMode.Resourece)
                return Resources.Load<T>(resPath);
            else
            {
#if UNITY_EDITOR
                if (AssetConfig.RuntimeMode == UpdateMode.Editor)
                    return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(assetPath);
#endif
                if (AssetBundleManifest == null)
                {
                    if (!AssetBundles.ContainsKey(AssetConfig.AssetBundleManifestName))
                        AssetBundles.Add(AssetConfig.AssetBundleManifestName,
                            AssetBundle.LoadFromFile(AssetConfig.AssetBundleRootPath + AssetConfig.AssetBundleManifestName));
                    AssetBundleManifest = AssetBundles[AssetConfig.AssetBundleManifestName].LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                }
                string[] dependencies = AssetBundleManifest.GetAllDependencies(assetBundleName);
                for (int i = 0; i < dependencies.Length; i++)
                {
                    if (!AssetBundles.ContainsKey(dependencies[i]))
                        AssetBundles.Add(dependencies[i], AssetBundle.LoadFromFile(AssetConfig.AssetBundleRootPath + dependencies[i]));
                }
                if (!AssetBundles.ContainsKey(assetBundleName))
                    AssetBundles.Add(assetBundleName, AssetBundle.LoadFromFile(AssetConfig.AssetBundleRootPath + assetBundleName));
               return AssetBundles[assetBundleName].LoadAsset<T>(assetPath);
            }
        }

        /// <summary>
        /// 同步加载Resources下的资源
        /// </summary>
        /// <typeparam name="T">资源类型</typeparam>
        /// <param name="resPath">资源路径</param>
        /// <returns></returns>
        public T LoadAsset<T>(string resPath) where T : UnityEngine.Object=> LoadAsset<T>(resPath, "","");

        /// <summary>
        /// 同步加载AssetBundle资源
        /// </summary>
        /// <typeparam name="T">资源类型</typeparam>
        /// <param name="assetPath">资源路径</param>
        /// <param name="assetBundleName">AB包名</param>
        /// <returns></returns>
        public T LoadAsset<T>(string assetPath,string assetBundleName) where T : UnityEngine.Object => LoadAsset<T>("", assetPath,assetBundleName,ResLoadMode.AssetBundle);


        /// <summary>
        /// 异步加载资源
        /// </summary>
        /// <typeparam name="T">资源类型</typeparam>
        /// <param name="resPath">资源路径</param>
        /// <param name="onLoading">加载中事件</param>
        /// <param name="onLoadDone">加载完成事件</param>
        /// <returns></returns>
        public Coroutine LoadAssetAsync<T>(LoadResHandle<T> resHandle)where T:UnityEngine.Object
        {
           return _main.StartCoroutine(LoadAsync(resHandle));
        }

        private IEnumerator LoadAsync<T>(LoadResHandle<T> resHandle)where T:UnityEngine.Object
        {
            UnityEngine.Object asset = null;
            if (resHandle.LoadMode == ResLoadMode.Resourece)
            {
                ResourceRequest request = Resources.LoadAsync<T>(resHandle.ResourcePath);
                while (!request.isDone)
                {
                    resHandle.OnLoading?.Invoke(request.progress);
                    yield return null;
                }
                resHandle.OnLoading?.Invoke(1);
                if (request.asset.IsNull())
                    throw new UnityException($"资源加载失败：Resources文件夹中不存在资源 {resHandle.ResourcePath}!");
                else
                    asset = request.asset;
            }
            else
            {
#if UNITY_EDITOR
                if (AssetConfig.RuntimeMode == UpdateMode.Editor)
                {
                    resHandle.OnLoading?.Invoke(1);
                    asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(resHandle.AssetPath);
                    if (asset)
                        asset = asset as T;
                    else
                        throw new UnityException($"加载资源失败：路径{resHandle.AssetPath}中不存在资源 {resHandle.AssetPath}！");
                }
                else
                {
                    //等待相关依赖资源的加载
                    yield return LoadDependenciesAssetBundleAsync(resHandle.AssetBundleName);
                    yield return LoadAssetBundleAsync(resHandle.AssetBundleName, resHandle.OnLoading);
                    if (AssetBundles.ContainsKey(resHandle.AssetBundleName.ToLower()))
                    {
                        //asset = AssetBundles[resHandle.AssetBundleName].LoadAsset<T>(resHandle.AssetPath);
                        AssetBundleRequest request = AssetBundles[resHandle.AssetBundleName.ToLower()].LoadAssetAsync<T>(resHandle.AssetPath);
                        yield return request;
                        while(!request.isDone)
                        {
                            resHandle.OnLoading(request.progress / 2+0.5f);
                            yield return null;
                        }
                        asset = request.asset;
                        if (!asset)
                            throw new UnityException(string.Format("加载资源失败：AB包 {0} 中不存在资源 {1}！", resHandle.AssetBundleName, resHandle.AssetPath));
                    }
                }
#else
                yield return LoadDependenciesAssetBundleAsync(resHandle.AssetBundleName);
                    yield return LoadAssetBundleAsync(resHandle.AssetBundleName, resHandle.OnLoading);
                    if (AssetBundles.ContainsKey(resHandle.AssetBundleName.ToLower()))
                    {
                        AssetBundleRequest request = AssetBundles[resHandle.AssetBundleName.ToLower()].LoadAssetAsync<T>(resHandle.AssetPath);
                        yield return request;
                        while(!request.isDone)
                        {
                            resHandle.OnLoading(request.progress / 2+0.5f);
                            yield return null;
                        }
                        asset = request.asset;
                        if (!asset)
                            throw new UnityException(string.Format("加载资源失败：AB包 {0} 中不存在资源 {1}！", resHandle.AssetBundleName, resHandle.AssetPath));
                    }
#endif
            }

            if (asset)
            {
                resHandle.OnLoadOver?.Invoke(asset as T);
            }
            else
                resHandle.OnLoadOver?.Invoke(null);

        }


        /// <summary>
        /// 异步加载场景
        /// </summary>
        /// <param name="scenePath">场景路径</param>
        /// <param name="onLoading">加载中事件</param>
        /// <param name="onLoadDone">加载完成事件</param>
        /// <returns></returns>
        public Coroutine LoadSceneAsync(LoadResHandle<Scene> resHandle, LoadSceneMode loadSceneMode= LoadSceneMode.Single)
        {
            return _main.StartCoroutine(LoadScene(resHandle,loadSceneMode));
        }

        private IEnumerator LoadScene(LoadResHandle<Scene> resHandle, LoadSceneMode loadSceneMode)
        {
            if (SceneManager.GetSceneByPath(resHandle.AssetPath) == CurrentScene)
            {
                Debug.LogWarning($"场景{resHandle.AssetPath}已加载！");
                yield break;
            }
            if (resHandle.LoadMode==ResLoadMode.Resourece)
            {
                AsyncOperation ao = SceneManager.LoadSceneAsync(resHandle.AssetPath, loadSceneMode);
                while (!ao.isDone)
                {
                    resHandle.OnLoading?.Invoke(ao.progress);
                    yield return null;
                }
            }
           else
            {
                yield return LoadDependenciesAssetBundleAsync(resHandle.AssetBundleName);
#if UNITY_EDITOR
                if (AssetConfig.RuntimeMode==UpdateMode.Editor)
                {
                    LoadSceneParameters parameters = new LoadSceneParameters()
                    {
                        loadSceneMode = LoadSceneMode.Single,
                        localPhysicsMode = LocalPhysicsMode.None
                    };
                    AsyncOperation ao = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(resHandle.AssetPath, parameters);
                    while (!ao.isDone)
                    {
                        resHandle.OnLoading?.Invoke(ao.progress);
                        yield return null;
                    }
                }
                else
                {
                    yield return LoadAssetBundleAsync(resHandle.AssetBundleName, resHandle.OnLoading);
                    AsyncOperation ao = SceneManager.LoadSceneAsync(resHandle.AssetPath, loadSceneMode);
                    while (!ao.isDone)
                    {
                        resHandle.OnLoading?.Invoke(ao.progress);
                        yield return null;
                    }
                }
#else
                    yield return LoadAssetBundleAsync(resHandle.AssetBundleName, resHandle.OnLoading);
                    AsyncOperation ao = SceneManager.LoadSceneAsync(resHandle.AssetPath, loadSceneMode);
                    while (!ao.isDone)
                    {
                        resHandle.OnLoading?.Invoke(ao.progress);
                        yield return null;
                    }
#endif
            }
            resHandle.OnLoading?.Invoke(1);
            CurrentScene = SceneManager.GetSceneByPath(resHandle.AssetPath);
            //SceneManager.SetActiveScene(CurrentScene);
            resHandle.OnLoadOver?.Invoke(CurrentScene);
        }

        /// <summary>
        /// 异步清理内存 回收垃圾
        /// </summary>
        /// <returns></returns>
        public Coroutine ClearMemoryAsync()
        {
            return _main.StartCoroutine(ClearMemory());
        }

        /// <summary>
        /// 清理内存，释放空闲内存（异步）
        /// </summary>
        /// <returns>协程迭代器</returns>
        private IEnumerator ClearMemory()
        {
            yield return Resources.UnloadUnusedAssets();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        /// <summary>
        /// 异步卸载资源
        /// </summary>
        /// <returns></returns>
        public Coroutine UnLoadAsstAsync(ResLoadMode resLoadMode, string assetBundleName, bool unloadAllLoadedObjects = false) 
        {
            return _main.StartCoroutine(UnLoadAsst(resLoadMode,assetBundleName,unloadAllLoadedObjects));
        }

        private IEnumerator UnLoadAsst(ResLoadMode resLoadMode, string assetBundleName, bool unloadAllLoadedObjects = false)
        {
            yield return UnLoadAsset(ResLoadMode.Resourece,assetBundleName,unloadAllLoadedObjects);
        }

        /// <summary>
        /// 异步卸载场景
        /// </summary>
        /// <param name="scene"></param>
        /// <returns></returns>
        public Coroutine UnloadSceneAsync(Scene scene,TAction<float> OnUnloading=null,TAction OnUnloadOver=null)
        {
            return _main.StartCoroutine(UnloadScene(scene,OnUnloading,OnUnloadOver));
        }

        private IEnumerator UnloadScene(Scene scene, TAction<float> OnUnloading, TAction OnUnloadOver)
        {
            AsyncOperation ao = SceneManager.UnloadSceneAsync(scene);
            while (!ao.isDone)
            {
                OnUnloading?.Invoke(ao.progress);
                yield return null;
            }
            OnUnloadOver?.Invoke();
            CurrentScene = SceneManager.GetActiveScene();

        }

        public IEnumerator UnLoadAsset(ResLoadMode resLoadMode, string assetBundleName, bool unloadAllLoadedObjects = false)
        {
            if (resLoadMode == ResLoadMode.Resourece)
            {
                yield return Resources.UnloadUnusedAssets();
            }
            else
            {
                if (AssetBundles.ContainsKey(assetBundleName))
                {
                    AssetBundles[assetBundleName].Unload(unloadAllLoadedObjects);
                    AssetBundles.Remove(assetBundleName);
                }
                yield return null;
            }
        }

        /// <summary>
        /// 异步加载AB包清单
        /// </summary>
        /// <returns>协程迭代器</returns>
        private IEnumerator LoadAssetBundleManifestAsync()
        {
            if (string.IsNullOrEmpty(AssetConfig.AssetBundleManifestName))
            {
                throw new UnityException("请设置资源配置文件的 AssetBundleManifestName 属性，为所有AB包提供依赖清单！");
            }
            else
            {
                if (AssetBundleManifest == null)
                {
                    yield return LoadAssetBundleAsync(AssetConfig.AssetBundleManifestName, null);

                    if (AssetBundles.ContainsKey(AssetConfig.AssetBundleManifestName))
                    {
                        AssetBundleManifest = AssetBundles[AssetConfig.AssetBundleManifestName].LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                        UnLoadAsset(ResLoadMode.AssetBundle, AssetConfig.AssetBundleManifestName);
                    }
                }
            }
            yield return null;
        }

        /// <summary>
        /// 异步加载AB包（提供进度回调）
        /// </summary>
        /// <param name="assetBundleName">AB包名称</param>
        /// <param name="onLoading">加载中事件</param>
        /// <param name="isManifest">是否是加载清单</param>
        /// <returns>协程迭代器</returns>
        private IEnumerator LoadAssetBundleAsync(string assetBundleName, TAction<float> onLoading)
        {
            assetBundleName = assetBundleName.ToLower();
            if (!AssetBundles.ContainsKey(assetBundleName))
            {
                AssetBundleCreateRequest abRequst = AssetBundle.LoadFromFileAsync(AssetConfig.AssetBundleRootPath + assetBundleName);

                yield return abRequst;

                while (!abRequst.isDone)
                {
                    onLoading?.Invoke(abRequst.progress/2);
                    yield return null;
                }

                if (abRequst.assetBundle)
                    AssetBundles.Add(assetBundleName, abRequst.assetBundle);
                else
                    throw new UnityException($" 未加载到AB包【{assetBundleName}】！");
            }
            onLoading?.Invoke(1);
        }

        /// <summary>
        /// 异步加载依赖AB包
        /// </summary>
        /// <param name="assetBundleName">AB包名称</param>
        /// <returns>协程迭代器</returns>
        private IEnumerator LoadDependenciesAssetBundleAsync(string assetBundleName)
        {
#if UNITY_EDITOR
            if (AssetConfig.RuntimeMode != UpdateMode.Editor)
            {
                yield return LoadAssetBundleManifestAsync();

                if (AssetBundleManifest != null)
                {
                    string[] dependencies = AssetBundleManifest.GetAllDependencies(assetBundleName);
                    foreach (string item in dependencies)
                    {
                        if (AssetBundles.ContainsKey(item))
                            continue;

                        yield return LoadAssetBundleAsync(item,null);
                    }
                }
            }
#else
                yield return LoadAssetBundleManifestAsync();

                if (AssetBundleManifest != null)
                {
                    string[] dependencies = AssetBundleManifest.GetAllDependencies(assetBundleName);
                    foreach (string item in dependencies)
                    {
                        if (AssetBundles.ContainsKey(item))
                        {
                            continue;
                        }

                        yield return LoadAssetBundleAsync(item,null);
                    }
                }
#endif
            yield return null;
        }
    }
}
